# number of users
user_number = 943

# number of movies
movie_number = 1682

# number of features
features_number = 20

# regularization coefficient
regularization_coeff=0

init_value = 0.1

init_value_q = init_value * init_value

# init users and movies feature vectors with default value
U = array(init_value, dim=c(user_number,features_number))
V = array(init_value, dim=c(movie_number,features_number))

# file with timestamp cut at the end of every line	
# command "sed 's/.........$//' sample.data > sample2.data"
# will suffice - cuts last 9 chars from every line of file
training_file = "/home/maciej/Pulpit/SEM9/MOW/ml-100k/test/u11.base"

# read train data set
train_data = read.table("/home/maciej/Pulpit/SEM9/MOW/ml-100k/test/u11.base", col.names=c("user_id", "movie_id", "rating"))

# add cache column for every rating
train_data$cache = 0

train_data = as.matrix(train_data)

test_file = "/home/maciej/Pulpit/SEM9/MOW/ml-100k/test/u11.test"

test_data = read.table(test_file, col.names=c("user_id", "movie_id", "rating"))

test_data = as.matrix(test_data)

learning_rate = 0.005

train_rmse_vec = c()
test_rmse_vec = c()

runExperiments = function(){
	
	for(f_nr in 1:1){
		features_number <<- f_nr		
		trainAllFeatures(train_data, U, V)
	}
	
	print(train_rmse_vec)
	print(test_rmse_vec)
	
}

calculateRmse = function(data, U, V, training_set){
	
	sum = 0
	if(training_set){
		
		for (i in 1:nrow(data)) {
			# true rating - predicted
			rating_diff = data[i, "rating"] - data[i, "cache"]   
			sum = sum + rating_diff * rating_diff	
		}
		
		return (as.numeric(sqrt(sum / nrow(data))))
	}else{
		for (i in 1:nrow(data)) {
			# true rating - predicted
			rating = 0
			for(fn in 1:features_number){
				rating = rating + U[data[i, "user_id"], fn] * V[data[i, "movie_id"], fn]
				if(rating > 5) rating = 5
				else if(rating < 1) rating = 1
			}
			rating_diff = data[i, "rating"] - rating
			sum = sum + rating_diff * rating_diff	
		}
		
		return (as.numeric(sqrt(sum / nrow(data))))		
	}
	
}

predictRating = function(user_feature_vector, movie_feature_vector, feature, cache, count_trailing_features){
	
	if(cache > 0)
		sum = cache
	else
		sum = 0
	
	sum = sum + user_feature_vector[feature] * movie_feature_vector[feature]

	if(sum > 5) sum = 5
	else if(sum < 1) sum = 1
	
	if(count_trailing_features){
		sum = sum + (features_number - feature) * init_value_q
		if(sum > 5) sum = 5
		else if(sum < 1) sum = 1
	}
	
	return (sum)
}


trainAllFeatures = function(train_data, U, V){
	
	start_time = Sys.time()
	
	# loop through all features
	for(feature in 1:features_number){
		
		# reset current RMSE for training of every new feature
		current_RMSE = 1.0
		flag = TRUE
		epoch_nr = 0
		
		repeat{
			
			epoch_nr = epoch_nr + 1
			feature_sum_error = 0
			previous_RMSE = current_RMSE
			
			# go trough all ratings and update feature values
			for (i in 1:nrow(train_data)) {	
				
				# compute difference between true rating and predicted
				diff = train_data[i, "rating"] - predictRating(U[train_data[i, "user_id"],], V[train_data[i, "movie_id"],], feature, train_data[i, "cache"], TRUE)
				feature_sum_error = feature_sum_error + diff * diff
				
				# compute gradients
				G1 = diff * V[train_data[i, "movie_id"], feature] - regularization_coeff * U[train_data[i, "user_id"], feature]
				G2 = diff * U[train_data[i, "user_id"], feature] - regularization_coeff * V[train_data[i, "movie_id"], feature]
				
				# compute new values of currently trained feature
				U[train_data[i, "user_id"], feature] = U[train_data[i, "user_id"], feature] + learning_rate * G1
				V[train_data[i, "movie_id"], feature] = V[train_data[i, "movie_id"], feature] + learning_rate * G2 
				
			}
			
			# calculate new RMSE
			current_RMSE = sqrt(feature_sum_error / nrow(train_data))			
			
			# if error starts to rise break training feature
			if(flag){
				flag = FALSE
			}else if(current_RMSE > previous_RMSE){
				break
			}else if(epoch_nr >= 120){
				break
			}
			
		} # repeat end
		
		# cache errors calculated from trained feature
		for (i in 1:nrow(train_data)){
			train_data[i, "cache"] = predictRating(U[train_data[i, "user_id"],], V[train_data[i, "movie_id"],], feature ,train_data[i, "cache"], FALSE)
		}
		
		cat(sprintf("End of training feature: %d\n", feature))		
		
	} # for end	

	print(Sys.time() - start_time)

	train_rmse_vec <<- c(train_rmse_vec, calculateRmse(train_data, U, V, TRUE))
	test_rmse_vec <<- c(test_rmse_vec, calculateRmse(test_data, U, V, FALSE))
}

runExperiments()